#include <partical/ParticleEmitter.h>

namespace ne
{
    ParticleEmitter::ParticleEmitter(const std::string &type)
        : Particle(),
          mEmitterType(type),
          mEmittedEmitterType(),
          mEmissionRate(0.0f),
          mStartTime(0.0f),
          mRemainStartTime(0.0f),
          mMinDuration(0.0f), mMaxDuration(0.0f),
          mRemainDuration(0.0f),
          mMinRepeatDelay(0.0f), mMaxRepeatDelay(0.0f),
          mRemainRepeatDelay(0.0f),
          mRemainInterval(0.0f),
          mMinMass(1.0f), mMaxMass(1.0f),
          mMinAngle(0.0f), mMaxAngle(Math::PI),
          mMinVelocity(0.0f), mMaxVelocity(0.0f),
          mMinRotation(0.0f), mMaxRotation(0.0f),
          mMinZoom(1.0f), mMaxZoom(1.0f),
          mStartColorRange(Color::WHITE),
          mEndColorRange(Color::WHITE),
          mMinLifetime(0.0f), mMaxLifetime(0.0f),
          mEnable(true),
          mEmitting(true)
    {
    }

    ParticleEmitter::ParticleEmitter(const ParticleEmitter &emitter)
        : Particle(emitter),
          mEmitterType(emitter.mEmitterType),
          mEmittedEmitterType(emitter.mEmittedEmitterType),
          mEmissionRate(emitter.mEmissionRate),
          mStartTime(emitter.mStartTime),
          mRemainStartTime(emitter.mRemainStartTime),
          mMinDuration(emitter.mMinDuration), mMaxDuration(emitter.mMinDuration),
          mRemainDuration(emitter.mRemainDuration),
          mMinRepeatDelay(emitter.mMinRepeatDelay), mMaxRepeatDelay(emitter.mMaxRepeatDelay),
          mRemainRepeatDelay(emitter.mRemainRepeatDelay),
          mRemainInterval(emitter.mRemainInterval),
          mMinMass(emitter.mMinMass), mMaxMass(emitter.mMaxMass),
          mMinAngle(emitter.mMinAngle), mMaxAngle(emitter.mMaxAngle),
          mMinVelocity(emitter.mMinVelocity), mMaxVelocity(emitter.mMaxVelocity),
          mMinRotation(emitter.mMinRotation), mMaxRotation(emitter.mMaxRotation),
          mMinZoom(emitter.mMinZoom), mMaxZoom(emitter.mMaxZoom),
          mStartColorRange(emitter.mStartColorRange),
          mEndColorRange(emitter.mEndColorRange),
          mMinLifetime(emitter.mMinLifetime), mMaxLifetime(emitter.mMaxLifetime),
          mEnable(emitter.mEnable),
          mEmitting(emitter.mEmitting)
    {
    }

    const std::string& ParticleEmitter::getEmitterType() const
    {
        return mEmitterType;
    }

    void ParticleEmitter::setEmittedEmitterType(const std::string &type)
    {
        mEmittedEmitterType = type;
    }

    const std::string& ParticleEmitter::getEmittedEmitterType() const
    {
        return mEmittedEmitterType;
    }

    void ParticleEmitter::setEmissionRate(const real rate)
    {
        mEmissionRate = rate;
    }

    real ParticleEmitter::getEmissionRate() const
    {
        return mEmissionRate;
    }

    void ParticleEmitter::setStartTime(const real v)
    {
        mStartTime = v;
        reset();
    }

    real ParticleEmitter::getStartTime() const
    {
        return mStartTime;
    }

    void ParticleEmitter::setDuration(const real v)
    {
        setDuration(v, v);
    }

    real ParticleEmitter::getDuration() const
    {
        return mMinDuration;
    }

    void ParticleEmitter::setDuration(const real minV, const real maxV)
    {
        mMinDuration = minV;
        mMaxDuration = maxV;
        _updateDurationAndRepeatDelay();
    }

    void ParticleEmitter::setMinDuration(const real v)
    {
        mMinDuration = v;
        _updateDurationAndRepeatDelay();
    }

    real ParticleEmitter::getMinDuration() const
    {
        return mMinDuration;
    }

    void ParticleEmitter::setMaxDuration(const real v)
    {
        mMaxDuration = v;
        _updateDurationAndRepeatDelay();
    }

    real ParticleEmitter::getMaxDuration() const
    {
        return mMaxDuration;
    }

    void ParticleEmitter::setRepeatDelay(const real v)
    {
        setRepeatDelay(v, v);
    }

    real ParticleEmitter::getRepeatDelay() const
    {
        return mMinRepeatDelay;
    }

    void ParticleEmitter::setRepeatDelay(const real minV, const real maxV)
    {
        mMinRepeatDelay = minV;
        mMaxRepeatDelay = maxV;
        _updateDurationAndRepeatDelay();
    }

    void ParticleEmitter::setMinRepeatDelay(const real v)
    {
        mMinRepeatDelay = v;
        _updateDurationAndRepeatDelay();
    }

    real ParticleEmitter::getMinRepeatDelay() const
    {
        return mMinRepeatDelay;
    }

    void ParticleEmitter::setMaxRepeatDelay(const real v)
    {
        mMaxRepeatDelay = v;
        _updateDurationAndRepeatDelay();
    }

    real ParticleEmitter::getMaxRepeatDelay() const
    {
        return mMaxRepeatDelay;
    }

    void ParticleEmitter::setMass(const real m)
    {
        setMass(m, m);
    }

    real ParticleEmitter::getMass() const
    {
        return mMinMass;
    }

    void ParticleEmitter::setMass(const real minM, const real maxM)
    {
        mMinMass = minM;
        mMaxMass = maxM;
    }

    void ParticleEmitter::setMinMass(const real m)
    {
        mMinMass = m;
    }

    real ParticleEmitter::getMinMass() const
    {
        return mMinMass;
    }

    void ParticleEmitter::setMaxMass(const real m)
    {
        mMaxMass = m;
    }

    real ParticleEmitter::getMaxMass() const
    {
        return mMaxMass;
    }

    void ParticleEmitter::setAngle(const real v)
    {
        setAngle(v, v);
    }

    real ParticleEmitter::getAngle() const
    {
        return mMinAngle;
    }

    void ParticleEmitter::setAngle(const real minV, const real maxV)
    {
        mMinAngle = minV;
        mMaxAngle = maxV;
    }

    void ParticleEmitter::setMinAngle(const real v)
    {
        mMinAngle = v;
    }

    real ParticleEmitter::getMinAngle() const
    {
        return mMinAngle;
    }

    void ParticleEmitter::setMaxAngle(const real v)
    {
        mMaxAngle = v;
    }

    real ParticleEmitter::getMaxAngle() const
    {
        return mMaxAngle;
    }

    void ParticleEmitter::setVelocity(const real v)
    {
        setVelocity(v, v);
    }

    real ParticleEmitter::getVelocity() const
    {
        return mMinVelocity;
    }

    void ParticleEmitter::setVelocity(const real minV, const real maxV)
    {
        mMinVelocity = minV;
        mMaxVelocity = maxV;
    }

    void ParticleEmitter::setMinVelocity(const real v)
    {
        mMinVelocity = v;
    }

    real ParticleEmitter::getMinVelocity() const
    {
        return mMinVelocity;
    }

    void ParticleEmitter::setMaxVelocity(const real v)
    {
        mMaxVelocity = v;
    }

    real ParticleEmitter::getMaxVelocity() const
    {
        return mMaxVelocity;
    }

    void ParticleEmitter::setRotation(const real v)
    {
        setRotation(v, v);
    }

    real ParticleEmitter::getRotation() const
    {
        return mMinRotation;
    }

    void ParticleEmitter::setRotation(const real minV, const real maxV)
    {
        mMinRotation = minV;
        mMaxRotation = maxV;
    }

    void ParticleEmitter::setMinRotation(const real v)
    {
        mMinRotation = v;
    }

    real ParticleEmitter::getMinRotation() const
    {
        return mMinRotation;
    }

    void ParticleEmitter::setMaxRotation(const real v)
    {
        mMaxRotation = v;
    }

    real ParticleEmitter::getMaxRotation() const
    {
        return mMaxRotation;
    }

    void ParticleEmitter::setZoom(const real v)
    {
        setZoom(v, v);
    }

    real ParticleEmitter::getZoom() const
    {
        return mMinZoom;
    }

    void ParticleEmitter::setZoom(const real minV, const real maxV)
    {
        mMinZoom = minV;
        mMaxZoom = maxV;
    }

    void ParticleEmitter::setMinZoom(const real v)
    {
        mMinZoom = v;
    }

    real ParticleEmitter::getMinZoom() const
    {
        return mMinZoom;
    }

    void ParticleEmitter::setMaxZoom(const real v)
    {
        mMaxZoom = v;
    }

    real ParticleEmitter::getMaxZoom() const
    {
        return mMaxZoom;
    }

    void ParticleEmitter::setColorRange(const Color &c)
    {
        setColorRange(c, c);
    }

    const Color& ParticleEmitter::getColorRange() const
    {
        return mStartColorRange;
    }

    void ParticleEmitter::setColorRange(const Color &sC, const Color &eC)
    {
        mStartColorRange = sC;
        mEndColorRange = eC;
    }

    void ParticleEmitter::setStartColorRange(const Color &c)
    {
        mStartColorRange = c;
    }

    const Color& ParticleEmitter::getStartColorRange() const
    {
        return mStartColorRange;
    }

    void ParticleEmitter::setEndColorRange(const Color &c)
    {
        mEndColorRange = c;
    }

    const Color& ParticleEmitter::getEndColorRange() const
    {
        return mEndColorRange;
    }

    void ParticleEmitter::setLifetime(const real v)
    {
        setLifetime(v, v);
    }

    real ParticleEmitter::getLifetime() const
    {
        return mMinLifetime;
    }

    void ParticleEmitter::setLifetime(const real minV, const real maxV)
    {
        mMinLifetime = minV;
        mMaxLifetime = maxV;
    }

    void ParticleEmitter::setMinLifetime(const real v)
    {
        mMinLifetime = v;
    }

    real ParticleEmitter::getMinLifetime() const
    {
        return mMinLifetime;
    }

    void ParticleEmitter::setMaxLifetime(const real v)
    {
        mMaxLifetime = v;
    }

    real ParticleEmitter::getMaxLifetime() const
    {
        return mMaxLifetime;
    }

    void ParticleEmitter::setEnable(const bool b)
    {
        mEnable = b;
        reset();
    }

    void ParticleEmitter::enable()
    {
        setEnable(true);
    }

    void ParticleEmitter::disable()
    {
        setEnable(false);
    }

    bool ParticleEmitter::isEnable() const
    {
        return mEnable;
    }

    void ParticleEmitter::setEmitting(const bool b)
    {
        mEmitting = b;
        _updateDurationAndRepeatDelay();
    }

    bool ParticleEmitter::isEmitting() const
    {
        return (mEnable && mEmitting);
    }

    bool ParticleEmitter::isParticleEmitter() const
    {
        return true;
    }

    void ParticleEmitter::reset()
    {
        mRemainStartTime = mStartTime;
        setEmitting(true);
    }

    void ParticleEmitter::initialParticle(Particle *pParticle)
    {
        if (pParticle)
        {
            genEmissionMass(pParticle->mass);
            genEmissionPosition(pParticle->position);
            genEmissionOrientation(pParticle->orientation);
            genEmissionScale(pParticle->scale);
            genEmissionColor(pParticle->color);
            genEmissionTexCoord(pParticle->texCoord);

            genEmissionVelocity(pParticle->direction, pParticle->velocity);
            genEmissionRotation(pParticle->axis, pParticle->rotation);
            genEmissionZoom(pParticle->zoom);

            genEmissionLifetime(pParticle->totalLifetime);

            pParticle->lifetime = 0.0f;
            pParticle->force = Vector3::ZERO;
        }
    }

    size_t ParticleEmitter::genEmissionParticleCount(const real interval)
    {
        if (mEnable)
        {
            if (mEmitting)
            {
                size_t count = 0;
                mRemainInterval += mEmissionRate * interval;
                count = (size_t)mRemainInterval;
                mRemainInterval -= (real)count;

                if (mMaxDuration > 0.0f)
                {
                    mRemainDuration -= interval;
                    if (mRemainDuration <= 0.0f)
                    {
                        setEmitting(false);
                    }
                }

                return count;
            }
            else
            {
                if (mMaxRepeatDelay)
                {
                    mRemainRepeatDelay -= interval;
                    if (mRemainRepeatDelay <= 0.0f)
                    {
                        setEmitting(true);
                    }
                    if (mRemainStartTime > 0.0f)
                    {
                        mRemainStartTime -= interval;
                        if (mRemainStartTime <= 0.0f)
                        {
                            setEmitting(true);
                            mRemainStartTime = 0.0f;
                        }
                    }
                }
            }
        }
        return 0;
    }

    void ParticleEmitter::genEmissionMass(real &m)
    {
        if (mMinMass == mMaxMass)
        {
            m = mMinMass;
        }
        else
        {
            m = Random::RandReal(mMinMass, mMaxMass);
        }
    }

    void ParticleEmitter::genEmissionPosition(Vector3 &v)
    {
        v = position;
    }

    void ParticleEmitter::genEmissionOrientation(Quaternion &q)
    {
        q = orientation;
    }

    void ParticleEmitter::genEmissionScale(Vector3 &s)
    {
        s = scale;
    }

    void ParticleEmitter::genEmissionColor(Color &c)
    {
        if (mStartColorRange == mEndColorRange)
        {
            c = mStartColorRange;
        }
        else
        {
            c = Color::Lerp(mStartColorRange, mEndColorRange, Random::RandReal());
        }
    }

    void ParticleEmitter::genEmissionTexCoord(Vector2 &t)
    {
        t = texCoord;
    }

    void ParticleEmitter::genEmissionVelocity(Vector3 &d, Vector3 &v)
    {
        real angle = 0.0f;
        if (mMinAngle == mMaxAngle)
        {
            angle = mMinAngle;
        }
        else
        {
            angle = Random::RandReal(mMinAngle, mMaxAngle);
        }

        if (angle > 0.0f)
        {
            // Generate direction (mDirection rotate angle relative newUp(axes))
            Vector3 dir = orientation * Vector3::UNIT_Z;
            Quaternion qDir(dir, angle);
            Vector3 up = orientation * Vector3::UNIT_Y;
            real upAngle = Random::RandReal(0.0f, Math::DOUBLE_PI);
            Quaternion qUp(up, upAngle);
            d = qUp * qDir * Vector3::UNIT_Y;
        }
        else
        {
            d = orientation * Vector3::UNIT_Z;
        }

        if (mMinVelocity == mMaxVelocity)
        {
            v = d * mMinVelocity;
        }
        else
        {
            v = d * Random::RandReal(mMinVelocity, mMaxVelocity);
        }
    }

    void ParticleEmitter::genEmissionRotation(Vector3 &a, real &r)
    {
        a = axis;
        if (mMinRotation == mMaxRotation)
        {
            r = mMinRotation;
        }
        else
        {
            r = Random::RandReal(mMinRotation, mMaxRotation);
        }
    }

    void ParticleEmitter::genEmissionZoom(real &z)
    {
        if (mMinZoom == mMaxZoom)
        {
            z = mMinZoom;
        }
        else
        {
            z = Random::RandReal(mMinZoom, mMaxZoom);
        }
    }

    void ParticleEmitter::genEmissionLifetime(real &l)
    {
        if (mMinLifetime == mMaxLifetime)
        {
            l = mMinLifetime;
        }
        else
        {
            l = Random::RandReal(mMinLifetime, mMaxLifetime);
        }
    }

    ParticleEmitter::~ParticleEmitter()
    {
    }

    void ParticleEmitter::_updateDurationAndRepeatDelay()
    {
        if (mEmitting)
        {
            if (mMinDuration == mMaxDuration)
            {
                mRemainDuration = mMinDuration;
            }
            else
            {
                mRemainDuration = Random::RandReal(mMinDuration, mMaxDuration);
            }
        }
        else
        {
            if (mMinRepeatDelay == mMaxRepeatDelay)
            {
                mRemainRepeatDelay = mMinRepeatDelay;
            }
            else
            {
                mRemainRepeatDelay = Random::RandReal(mMinRepeatDelay, mMaxRepeatDelay);
            }
        }
    }
}
